package cz.semjobKB.virtuoso.dao;

import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import cz.semjobKB.virtuoso.document.api.IDocumentDetail;
import cz.semjobKB.virtuoso.document.api.IResourceDetail;
import cz.semjobKB.virtuoso.document.api.ITerminologyDetail;
import cz.semjobKB.virtuoso.document.api.ITripleDbItem;
import cz.semjobKB.virtuoso.document.impl.DocumentDetail;
import cz.semjobKB.virtuoso.document.impl.ResourceDetail;
import cz.semjobKB.virtuoso.document.impl.TerminologyDetail;
import cz.semjobKB.virtuoso.document.impl.TripleDbItem;
import cz.semjobKB.virtuoso.utils.VirtuosoUtils;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import virtuoso.jena.driver.VirtGraph;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Component
public class GraphDetails {

	@Autowired
	VirtuosoUtils virtuosoUtils;

	@Autowired
	VirtuosoDao virtuosoDao;

	private static Logger logger = Logger.getLogger(GraphDetails.class);

	@Value( "${db.url}" )
	private String dbUrl;

	@Value( "${db.user}" )
	private String dbUser;

	@Value( "${db.pass}" )
	private String dbPass;

	public IDocumentDetail getDocumentDetail(String documentName) {

		Node baseDocumentPrefixNode = Node.createURI(virtuosoUtils.getKnownGraphsUris().get("document"));

		Node documentUri;
		if (documentName.startsWith(baseDocumentPrefixNode.getURI())) {
			documentUri = Node.createURI(documentName);
			documentName = documentName.replace(baseDocumentPrefixNode.getURI(), "");
		} else {
			documentUri = Node.createURI(baseDocumentPrefixNode.getURI() + documentName);
		}

		VirtGraph documentGraph = new VirtGraph(documentUri.getURI(), dbUrl, dbUser, dbPass);

		if(documentGraph.isEmpty()) {
			return null;
		}

		Node ontoFrequencyNode = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasFrequency"));
		Node ontoHasName = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasName"));
		Node ontoHasLocation = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasLocation"));

		Node ontoHasTerminologyCount = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasTerminologyCount"));
		Node ontoHasSentenceCount = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasSentenceCount"));
		Node ontoRdfType = Node.createURI(virtuosoUtils.getOntologyNodes().get("a"));		

		List<ITerminologyDetail> terminologyList = new ArrayList<ITerminologyDetail>();
		List<ITripleDbItem> triplesList = new ArrayList<ITripleDbItem>();

		String count = "SELECT DISTINCT ?g WHERE { GRAPH ?g { ?s ?p ?o} . FILTER regex(?g, \"" + baseDocumentPrefixNode.getURI() +"\")}";
		ResultSet resultCount = virtuosoDao.executeQuery(count, documentGraph);
		Integer numberOfDocument = 0;
		while (resultCount.hasNext()) {			
			QuerySolution solution = resultCount.nextSolution();
			if (solution.getResource("?g").getURI().startsWith(baseDocumentPrefixNode.getURI())) {
				numberOfDocument++;
			}
		}
		
		String query = "SELECT * FROM <" + documentGraph.getGraphName() + "> WHERE { ?s ?p ?o }";
		ResultSet result = virtuosoDao.executeQuery(query, documentGraph);

		String documentGraphName = "";
		String documentLocation = "";		
		Integer sentenceCount = 0;
		Integer terminologyTotalCount = 0;
		Set<String> uniqueTerms = new HashSet<String>();
		Set<String> uniquePredicate = new HashSet<String>();
		Set<String> uniqueTerminology = new HashSet<String>();

		while(result.hasNext()) {
			QuerySolution resultQuery = result.nextSolution();
			Node subject = resultQuery.get("s").asNode();
			Node predicate = resultQuery.get("p").asNode();
			Node object = resultQuery.get("o").asNode();

			if(ontoHasName.equals(predicate)) {
				documentGraphName = (String)object.getLiteralValue();
				continue;
			}

			if(ontoHasLocation.equals(predicate)) {
				documentLocation = (String)object.getLiteralValue();
				continue;
			}			

			if(ontoHasTerminologyCount.equals(predicate)) {				
				continue;
			}

			if(ontoHasSentenceCount.equals(predicate)) {
				sentenceCount = Integer.valueOf((String)object.getLiteralValue());
				continue;
			}

			if(ontoFrequencyNode.equals(predicate)) {
				Integer frequency = Integer.valueOf((String)object.getLiteralValue());
				terminologyTotalCount += frequency;
				ITerminologyDetail terminologyDetail = getTerminologyDetail(subject.getURI(), frequency); 
				terminologyList.add(terminologyDetail);
				uniqueTerminology.add(terminologyDetail.getResourceLemma());
				continue;
			}
			if(ontoRdfType.equals(predicate)) {
				continue;
			}

			if (subject.getURI().startsWith(virtuosoUtils.getKnownGraphsUris().get("document"))) {
				continue;
			}
			IResourceDetail subjectDetail = getTermDetail(subject.getURI());
			uniqueTerms.add(subjectDetail.getResourceLemma());
			IResourceDetail predicateDetail = getPredicateDetail(predicate.getURI());
			uniquePredicate.add(predicateDetail.getResourceLemma());			
			IResourceDetail objectDetail = getTermDetail(object.getURI());
			uniqueTerms.add(objectDetail.getResourceLemma());
			triplesList.add(new TripleDbItem(subjectDetail, predicateDetail, objectDetail));			
		}
		File documentFile = new File(documentLocation);
		String documentTextFilePath = documentFile.getParent() + File.separator + "txt" + File.separator  + documentName + ".txt";

		String documentText = "";
		try {
			documentText = FileUtils.readFileToString(new File(documentTextFilePath));
		} catch (IOException e) {
			logger.error("Could not find text file: " + documentTextFilePath) ;
		}
		
		for (ITerminologyDetail termDetail : terminologyList) {
			double tf = (double)termDetail.getFrequency() / (double)terminologyTotalCount;
			double idf = Math.log10((double)numberOfDocument / (double)termDetail.getTotalCount());
			if (idf < 0) {
				idf = 0;
			}
			termDetail.setRelevance(tf * idf);
		}

		return new DocumentDetail(documentGraphName, documentLocation, documentText, triplesList, terminologyList, uniqueTerms.size(), uniquePredicate.size(), terminologyTotalCount, sentenceCount);
	}
	
	/**
	 * Finds every match in object position from term and predicate graph. It searches in string representation of the resource
	 * as well as in lemma representation. The matched Uri's are gathered in the set to avoid duplicity.
	 * The Resource details is then created for each uri and returned in List.
	 * @param name Resource name, it can be lemma or string
	 * @return List of matched resources by the given name
	 */
	public List<IResourceDetail> getResourceDetail(String name, boolean isUri) {
			
		if (isUri) {
			Node basePredPrefixNode = Node.createURI(virtuosoUtils.getKnownGraphsUris().get("predicates"));
			Node baseTermPrefixNode = Node.createURI(virtuosoUtils.getKnownGraphsUris().get("terms"));
			List<IResourceDetail> resourceDetailList = new ArrayList<IResourceDetail>();
			
			if (name.startsWith(baseTermPrefixNode.getURI())) {
				resourceDetailList.add(getTermDetail(name));
			} else if(name.startsWith(basePredPrefixNode.getURI())) {
				resourceDetailList.add(getPredicateDetail(name));
			}
			
			return resourceDetailList;
		}
		VirtGraph termGraph = new VirtGraph(virtuosoUtils.getKnownGraphsUris().get("terms"), dbUrl, dbUser, dbPass);
			
		String query =  "SELECT ?s FROM <" + termGraph.getGraphName() + "> WHERE { ?s ?p \"" + virtuosoUtils.escape(name)  + "\"}";
		
		ResultSet result = virtuosoDao.executeQuery(query, termGraph);
		
		Set<String> matchedUris = new HashSet<String>();
		
		while(result.hasNext()) {
			QuerySolution resultQuery = result.nextSolution();			
			Node matchedNode = resultQuery.get("s").asNode();
			matchedUris.add(matchedNode.getURI());
		}
		
		VirtGraph predicateGraph = new VirtGraph(virtuosoUtils.getKnownGraphsUris().get("predicates"), dbUrl, dbUser, dbPass);		
		query =  "SELECT ?s FROM <" + predicateGraph.getGraphName() + "> WHERE { ?s ?p \"" + virtuosoUtils.escape(name)  + "\"}";
		
		result = virtuosoDao.executeQuery(query, termGraph);
		
		while(result.hasNext()) {
			QuerySolution resultQuery = result.nextSolution();			
			Node matchedNode = resultQuery.get("s").asNode();
			matchedUris.add(matchedNode.getURI());
		}
		
		Node basePredPrefixNode = Node.createURI(virtuosoUtils.getKnownGraphsUris().get("predicates"));
		Node baseTermPrefixNode = Node.createURI(virtuosoUtils.getKnownGraphsUris().get("terms"));
		List<IResourceDetail> resourceDetailList = new ArrayList<IResourceDetail>();
		for (String uri : matchedUris) {
			if (uri.startsWith(baseTermPrefixNode.getURI())) {
				resourceDetailList.add(getTermDetail(uri));
			} else if(uri.startsWith(basePredPrefixNode.getURI())) {
				resourceDetailList.add(getPredicateDetail(uri));
			}
		}
		return resourceDetailList;		
	}
	
	public List<ITerminologyDetail> getTerminologyDetail(String name) {
		
		VirtGraph terminologyGraph = new VirtGraph(virtuosoUtils.getKnownGraphsUris().get("terminology"), dbUrl, dbUser, dbPass);
		
		String query =  "SELECT ?s FROM <" + terminologyGraph.getGraphName() + "> WHERE { ?s ?p \"" + virtuosoUtils.escape(name)  + "\"}";
		
		ResultSet result = virtuosoDao.executeQuery(query, terminologyGraph);
		
		Set<String> matchedUris = new HashSet<String>();
		
		while(result.hasNext()) {
			QuerySolution resultQuery = result.nextSolution();			
			Node matchedNode = resultQuery.get("s").asNode();
			matchedUris.add(matchedNode.getURI());
		}
		
		Node baseTerminologyPrefixNode = Node.createURI(virtuosoUtils.getKnownGraphsUris().get("terminology"));
		List<ITerminologyDetail> terminologyDetailList = new ArrayList<ITerminologyDetail>();
		
		for (String uri : matchedUris) {
			if (uri.startsWith(baseTerminologyPrefixNode.getURI())) {
				terminologyDetailList.add(getTerminologyDetail(uri, 0));
			} 
		}		
		return terminologyDetailList;	
		
	}

	private ITerminologyDetail getTerminologyDetail(String uri, Integer frequency) {

		VirtGraph terminologyGraph = new VirtGraph(virtuosoUtils.getKnownGraphsUris().get("terminology"), dbUrl, dbUser, dbPass);

		Node terminologyNode = Node.createURI(virtuosoUtils.escape(uri));
		Node ontoHasStringRepresentationNode = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasStringRepresentation"));
		Node ontoLanguageNode = Node.createURI(virtuosoUtils.getOntologyNodes().get("language"));
		Node ontoTotalCountNode = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasTotalCount"));
		Node ontoOverallFrequencyNode = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasOverallFrequency"));
		Node ontoHasLemmaNode = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasLemma"));

		String query =  "SELECT * FROM <" + terminologyGraph.getGraphName() + "> WHERE { <" + 
				terminologyNode.getURI() + "> ?p ?o }";

		ResultSet result = virtuosoDao.executeQuery(query, terminologyGraph);

		List<String> terminologyTextRepresentation = new ArrayList<String>();
		String lemma = "";
		String language = "";
		Integer overallFrequency = 0;
		Integer totalCount = 0;
		while(result.hasNext()) {
			QuerySolution resultQuery = result.nextSolution();			
			Node predicate = resultQuery.get("p").asNode();
			Node object = resultQuery.get("o").asNode();

			if (ontoHasLemmaNode.equals(predicate)) {
				lemma = (String)object.getLiteralValue();
				continue;
			}
			if (ontoHasStringRepresentationNode.equals(predicate)) {
				terminologyTextRepresentation.add((String)object.getLiteralValue());
				continue;
			}
			if (ontoLanguageNode.equals(predicate)) {
				language = (String)object.getLiteralValue();
				continue;
			}
			if (ontoTotalCountNode.equals(predicate)) {
				totalCount = Integer.valueOf((String)object.getLiteralValue());
				continue;
			}
			if (ontoOverallFrequencyNode.equals(predicate)) {
				overallFrequency = Integer.valueOf((String)object.getLiteralValue());
			}
		}
		
		return new TerminologyDetail(
				new ResourceDetail(uri, lemma, terminologyTextRepresentation, language),
				frequency, overallFrequency, totalCount);
	}

	private IResourceDetail getTermDetail(String uri) {

		VirtGraph termGraph = new VirtGraph(virtuosoUtils.getKnownGraphsUris().get("terms"), dbUrl, dbUser, dbPass);

		Node baseTermPrefixNode = Node.createURI(virtuosoUtils.getKnownGraphsUris().get("terms"));		
		Node termNode;
		if (!uri.startsWith(baseTermPrefixNode.getURI())) {
			termNode = Node.createURI(baseTermPrefixNode + uri);
		} else {
			termNode = Node.createURI(virtuosoUtils.escape(uri));
		}		

		Node ontoHasLemmaNode = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasLemma"));
		Node ontoHasStringRepresentationNode = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasStringRepresentation"));		
		Node ontoLanguageNode = Node.createURI(virtuosoUtils.getOntologyNodes().get("language"));

		String query =  "SELECT * FROM <" + termGraph.getGraphName() + "> WHERE { <" + 
				termNode.getURI() + "> ?p ?o }";

		ResultSet result = virtuosoDao.executeQuery(query, termGraph);

		List<String> termTextRepresentation = new ArrayList<String>();
		String lemma = "";
		String language = "";
		while(result.hasNext()) {
			QuerySolution resultQuery = result.nextSolution();			
			Node predicate = resultQuery.get("p").asNode();
			Node object = resultQuery.get("o").asNode();

			if (ontoHasLemmaNode.equals(predicate)) {
				lemma = (String)object.getLiteralValue();
				continue;
			}
			if (ontoHasStringRepresentationNode.equals(predicate)) {
				termTextRepresentation.add((String)object.getLiteralValue());
				continue;
			}
			if (ontoLanguageNode.equals(predicate)) {
				language = (String)object.getLiteralValue();				
			}			
		}

		return new ResourceDetail(uri, lemma, termTextRepresentation, language);
	}

	private IResourceDetail getPredicateDetail(String uri) {

		VirtGraph predicateGraph = new VirtGraph(virtuosoUtils.getKnownGraphsUris().get("predicates"), dbUrl, dbUser, dbPass);

		Node basePredPrefixNode = Node.createURI(virtuosoUtils.getKnownGraphsUris().get("predicates"));
		Node predicateNode;
		if (!uri.startsWith(basePredPrefixNode.getURI())) {
			predicateNode = Node.createURI(basePredPrefixNode + uri);
		} else {
			predicateNode = Node.createURI(virtuosoUtils.escape(uri));
		}		

		Node ontoHasLemmaNode = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasLemma"));
		Node ontoHasStringRepresentationNode = Node.createURI(virtuosoUtils.getOntologyNodes().get("hasStringRepresentation"));		
		Node ontoLanguageNode = Node.createURI(virtuosoUtils.getOntologyNodes().get("language"));

		String query =  "SELECT * FROM <" + predicateGraph.getGraphName() + "> WHERE { <" + 
				predicateNode.getURI() + "> ?p ?o }";

		ResultSet result = virtuosoDao.executeQuery(query, predicateGraph);

		List<String> predicateTextRepresentation = new ArrayList<String>();
		String lemma = "";
		String language = "";
		while(result.hasNext()) {
			QuerySolution resultQuery = result.nextSolution();			
			Node predicate = resultQuery.get("p").asNode();
			Node object = resultQuery.get("o").asNode();

			if (ontoHasLemmaNode.equals(predicate)) {
				lemma = (String)object.getLiteralValue();
				continue;
			}
			if (ontoHasStringRepresentationNode.equals(predicate)) {
				predicateTextRepresentation.add((String)object.getLiteralValue());
				continue;
			}
			if (ontoLanguageNode.equals(predicate)) {
				language = (String)object.getLiteralValue();				
			}			
		}

		return new ResourceDetail(uri, lemma, predicateTextRepresentation, language);
	}
}
